package internal

import (
	"encoding/json"
	"io"
	"log"
	"os"
	"sync"
)

type urlStore struct {
	urls map[string]string
	mu   sync.RWMutex
	file *os.File
}

func NewUrlStore(fileName string) *urlStore {
	u := &urlStore{urls: make(map[string]string)}
	file, err := os.OpenFile(fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		log.Fatalln("file open error:", err)
	}
	u.file = file
	if err := u.load(); err != nil {
		log.Fatalln("file load error:", err)
	}
	return u
}

type record struct {
	Key string `json:"key"`
	Url string `json:"url"`
}

func (u *urlStore) set(key, url string) bool {
	u.mu.Lock()
	defer u.mu.Unlock()
	if _, present := u.urls[key]; present {
		return false
	}
	u.urls[key] = url
	return true
}

func (u *urlStore) Get(key string) string {
	u.mu.RLock()
	defer u.mu.RUnlock()
	if url, present := u.urls[key]; present {
		return url
	}
	return ""
}

func (u *urlStore) Put(url string) string {
	key := genKey(4)
	if ok := u.set(key, url); ok {
		if err := u.save(key, url); err != nil {
			log.Fatalln("save error:", err)
		}
		return key
	}
	return ""
}

func (u *urlStore) save(key, url string) error {
	encoder := json.NewEncoder(u.file)
	return encoder.Encode(record{key, url})
}

func (u *urlStore) load() error {
	//if _, err := u.file.Seek(0, 0);err != nil {
	//	return err
	//}
	decoder := json.NewDecoder(u.file)
	var err error
	for err == nil {
		var r record
		if err = decoder.Decode(&r); err == nil {
			u.urls[r.Key] = r.Url
		}
	}
	if err == io.EOF {
		return nil
	}
	return err
}
